package cn.emailChat.ai.service.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHeaders;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;


/**
 * @Description 使用DeepSeek实现摘要生成
 * @Author susu
 * @Date 2025/8/20
 */


@Slf4j
@Service
public class DeepSeekSummarizer implements Summarizer {

    // ---- 配置项（application.yml 可覆盖） ----
    @Value("${deepseek.api-base:https://api.deepseek.com}")
    private String apiBase;

    @Value("${deepseek.model:deepseek-chat}")
    private String model;

    @Value("${deepseek.api-key}")
    private String apiKey;

    @Value("${deepseek.timeout-ms:12000}")
    private int timeoutMs;

    private final ObjectMapper mapper = new ObjectMapper();

    // ========== Summarizer 接口实现 ==========
    @Override
    public String updateThreadSummary(String oldSummary, String lastUser, String lastAssistant) {
        // 长度控制，避免把超长正文全丢给模型
        String oldS = nz(oldSummary);
        String u = limit(nz(lastUser), 1000);
        String a = limit(nz(lastAssistant), 1000);

        String system = """
                你是邮件会话的“线程摘要器”。请基于“旧摘要 + 新增往来（用户提问/我们回复）”生成【更新后的线程摘要】：
                - 先用一句话给出背景/目标
                - 然后分条：已达成共识、待办与负责人/截止时间、未决/风险
                - 只保留事实与最新口径；如有变更请清楚说明
                - 200~400 个中文字符，简洁、去重复、可直接给模型使用
                - 仅输出摘要正文，不要任何额外说明
                """;

        String user = "旧摘要：\n" + (oldS.isBlank() ? "（无）" : oldS) +
                "\n\n新增往来：\n- 来信：" + u + "\n- 我方回复：" + a;

        try {
            String draft = chat(system, user);
            return postProcessSummary(draft, 400);
        } catch (Exception ex) {
            log.warn("[DeepSeekSummarizer] 调用失败，用本地兜底摘要: {}", ex.toString());
            // 兜底：朴素合并
            return fallbackMerge(oldS, u, a, 380);
        }
    }

    @Override
    public String distillToLongTerm(String threadSummary) {
        String s = limit(nz(threadSummary), 1200);

        String system = """
                你是“联系人长期画像”的提炼器。请将给定的【线程摘要】提炼为适合跨线程复用的长期要点：
                - 保留对该联系人的稳定偏好（语言、称呼、期望响应时间等）
                - 保留有持续效力的承诺/约束（价格口径、生效/失效日期、交付时间等）
                - 去掉只与本线程短期相关的细节
                - 100~300 个中文字符；仅输出提炼后的段落
                """;
        String user = "线程摘要：\n" + s;

        try {
            String draft = chat(system, user);
            return postProcessSummary(draft, 300);
        } catch (Exception ex) {
            log.warn("[DeepSeekSummarizer] distill 调用失败，返回原摘要片段: {}", ex.toString());
            return limit(s, 280);
        }
    }

    // ========== 调用 DeepSeek Chat Completions ==========
    private String chat(String system, String user) throws Exception {
        String url = normalizeUrl(apiBase, "/v1/chat/completions");

        Map<String, Object> req = new LinkedHashMap<>();
        req.put("model", model);
        // messages
        List<Map<String, String>> msgs = new ArrayList<>();
        msgs.add(msg("system", system));
        msgs.add(msg("user", user));
        req.put("messages", msgs);

        String body = mapper.writeValueAsString(req);

        RequestConfig cfg = RequestConfig.custom()
                .setConnectTimeout(timeoutMs)
                .setConnectionRequestTimeout(timeoutMs)
                .setSocketTimeout(timeoutMs)
                .build();

        try (CloseableHttpClient http = HttpClients.custom()
                .setDefaultRequestConfig(cfg).build()) {

            HttpPost post = new HttpPost(url);
            post.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey.trim());
            post.setHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=utf-8");
            post.setEntity(new StringEntity(body, ContentType.APPLICATION_JSON.withCharset(StandardCharsets.UTF_8)));

            try (CloseableHttpResponse resp = http.execute(post)) {
                int code = resp.getStatusLine().getStatusCode();
                InputStream is = resp.getEntity() != null ? resp.getEntity().getContent() : null;

                if (code / 100 != 2) {
                    String err = (is == null) ? ("HTTP " + code) : new String(is.readAllBytes(), StandardCharsets.UTF_8);
                    throw new RuntimeException("HTTP " + code + " - " + err);
                }

                String json = is == null ? "" : new String(is.readAllBytes(), StandardCharsets.UTF_8);
                JsonNode root = mapper.readTree(json);
                JsonNode choice0 = root.path("choices").path(0).path("message").path("content");
                String content = choice0.isMissingNode() ? null : choice0.asText();
                if (content == null || content.isBlank()) {
                    throw new RuntimeException("empty content from DeepSeek");
                }
                return content.trim();
            }
        }
    }

    // ========== 小工具 ==========
    private static Map<String, String> msg(String role, String content) {
        Map<String, String> m = new HashMap<>();
        m.put("role", role);
        m.put("content", content);
        return m;
    }

    private static String nz(String s) { return s == null ? "" : s.trim(); }

    private static String limit(String s, int maxChars) {
        if (s == null) return "";
        if (s.length() <= maxChars) return s;
        return s.substring(0, Math.max(0, maxChars)) + "…";
    }

    private static String normalizeUrl(String base, String path) {
        String b = base == null ? "" : base.trim();
        String p = path == null ? "" : path.trim();
        if (b.endsWith("/")) b = b.substring(0, b.length() - 1);
        if (!p.startsWith("/")) p = "/" + p;
        return b + p;
    }

    // 简单去噪：删除多余空行/项目符号，限制长度
    private static String postProcessSummary(String s, int maxChars) {
        if (s == null) return "";
        String t = s.replaceAll("[\\u200B-\\u200D\\uFEFF]", "")  // 零宽字符
                .replaceAll("[\\r\\t]+", " ")
                .replaceAll("\\n{3,}", "\n\n")
                .trim();
        return limit(t, maxChars);
    }

    private static String fallbackMerge(String oldS, String u, String a, int maxChars) {
        String merged = (oldS.isBlank() ? "" : ("【延续】" + oldS + "\n"))
                + "【新增】\n- 来信：" + limit(u, 280) + "\n- 我方：" + limit(a, 280);
        return limit(merged, maxChars);
    }
}
